package wall

import (
	"encoding/binary"
	"fmt"
	"net"

	"gitee.com/haodreams/libs/easy"
	"gitee.com/haodreams/libs/sock"
)

// 有隔离的数据读取
type Reader struct {
	ackChar   byte   //确认字符
	timeoutMs int64  //超时时间,单位毫秒
	head      []byte //包头2字节
	withWall  bool   //是否开启隔离
	conn      *net.TCPConn
	easy.Buffer
	buf []byte
}

func NewReader(conn *net.TCPConn, timeoutMs int64) *Reader {
	m := new(Reader)
	m.Setup().SetConn(conn).SetTimeout(timeoutMs)
	return m
}

func (m *Reader) Setup() *Reader {
	m.head = make([]byte, 2)
	m.buf = make([]byte, MaxSize+4)
	m.Buffer.Resize(MaxSize + 2).SetBigEndian(true)
	return m
}

func (m *Reader) SetAckChar(ackChar byte) *Reader {
	m.ackChar = ackChar
	m.withWall = true
	return m
}

func (m *Reader) SetTimeout(timeoutMs int64) *Reader {
	m.timeoutMs = min(max(timeoutMs, 1000), 120*1000)
	return m
}

func (m *Reader) SetConn(conn *net.TCPConn) *Reader {
	m.conn = conn
	return m
}

func (m *Reader) HaveWall() bool {
	return m.withWall
}

func (m *Reader) RemoteAddr() string {
	return m.conn.RemoteAddr().String()
}

func (m *Reader) Close() error {
	return m.conn.Close()
}

// 读取一个完整的数据包, 数据包长度不能超过64k
func (m *Reader) ReadPacket() (data []byte, err error) {
	m.Buffer.Reset()
	n,err:= m.Read(m.buf)
	if err != nil{
		return
	}
	//读取数据
	if n < 4{
		err = fmt.Errorf("读取数据长度错误")
		return
	}

	l:= int(binary.BigEndian.Uint32(m.buf))
	m.Buffer.Resize(l)
	m.Write(m.buf[4:n])
	for m.Buffer.Len() < l{
		_, err = m.Buffer.ReadFrom(m)
		if err != nil{
			return
		}
	}
	data = m.Buffer.Bytes()
	return
}

// 读一次完整的数据包,不能并发执行, 数据包长度不能超过64k
func (m *Reader) Read(buf []byte) (n int, err error) {
	//读取2个字节的报文头长度
	_, err = sock.ReadTimeoutMs(m.conn, m.head, int(m.timeoutMs))
	if err != nil {
		return
	}

	//读取指定长度的数据
	l := binary.BigEndian.Uint16(m.head)
	n, err = sock.ReadTimeoutMs(m.conn, buf[:int(uint(l))], int(m.timeoutMs))
	if err != nil {
		return
	}

	if m.withWall {
		_, err = m.conn.Write([]byte{m.ackChar})
	}

	return
}
